function Color(r, g, b) {
	this.r = r;
	this.g = g;
	this.b = b;
}

function PickerParams(hueR, hueG, hueB, valueR, valueG, valueB) {
	this.hueR = hueR;
	this.hueG = hueG;
	this.hueB = hueB;
	this.valueR = valueR;
	this.valueG = valueG;
	this.valueB = valueB;
}

function ColorPicker(canvasId, params) {
	this.canvasId = canvasId;
	this.params = params;
}

// An array of all color pickers that have been created so far.
var colorPickers = new Array();

function initializeColorPickerCanvas(canvasId, svWidth, hueWidth, bodyHeight, swatchHeight,
		hueR, hueG, hueB, valueR, valueG, valueB) {
	
	// Create the processing instance.
	var canvas = document.getElementById(canvasId);  
	var processingInstance = new Processing(canvas, renderProcessing);

	// Flag to indicate whether a new render is required.
	var pixelsStale = true;
	
	// Current values for the chosen hue and overall color value.
	var params = new PickerParams(hueR, hueG, hueB, hueR, hueG, hueB);
	colorPickers[colorPickers.length] = new ColorPicker(canvasId, params);
	
	// Hue colors to interpolate between.
	var colors = new Array();
	colors[0] = new Color(255, 0, 0);
	colors[1] = new Color(255, 0, 255);
	colors[2] = new Color(0, 0, 255);
	colors[3] = new Color(0, 255, 255);
	colors[4] = new Color(0, 255, 0);
	colors[5] = new Color(255, 255, 0);
	colors[6] = new Color(255, 0, 0);

	var lastClickedX = -10;
	var lastClickedY = -10;

	// Compute the color of a pixel on the value saturation square.
	function computeValueColor(x, y) {
		var px = x / svWidth;
		var py = y / bodyHeight;
	
		var r = params.hueR + ((255 - params.hueR) * (1 - px));
		var g = params.hueG + ((255 - params.hueG) * (1 - px));
		var b = params.hueB + ((255 - params.hueB) * (1 - px));	
		r = r - (r * py);
		g = g - (g * py);
		b = b - (b * py);
		return new Color(Math.floor(r), Math.floor(g), Math.floor(b));
	}

	// Compute the color of a pixel on the hue control.
	function computeHueColor(x, y) {
		var yp = y / bodyHeight;
		var nColors = colors.length - 1;
		
		// Choose the two colors to interpolate between.
		var firstColorIndex = Math.floor(nColors * yp);
		var secondColorIndex = firstColorIndex + 1;
		if (secondColorIndex > nColors) {
			secondColorIndex = 0;
		}	
		var first = colors[firstColorIndex];
		var second = colors[secondColorIndex];
		
		// Compute the percentage between the two colors.
		var bandWidth = bodyHeight / nColors;
		var localY = y % bandWidth;
		var localP = localY / bandWidth;
		
		// Interpolate!
		var r = second.r * localP + first.r * (1 - localP);
		var g = second.g * localP + first.g * (1 - localP);
		var b = second.b * localP + first.b * (1 - localP);	
		return new Color(Math.floor(r), Math.floor(g), Math.floor(b));
	}

	// Sets the current value color channels.
	// Called when mouse down on the value saturation square.
	function setValueColor(x, y) {
		var color = computeValueColor(x, y);		
		params.valueR = color.r;
		params.valueG = color.g;
		params.valueB = color.b;	
		pixelsStale = true;
	}
	
	// Sets the current value hue channels.
	// Called when mouse down on the hue control.
	function setHueColor(x, y) {
		var color = computeHueColor(x, y);	
		params.hueR = color.r;
		params.hueG = color.g;
		params.hueB = color.b;
		
		// Setting the hue always updates the value as well
		params.valueR = color.r;
		params.valueG = color.g;
		params.valueB = color.b;
		pixelsStale = true;
	}
	
	// The main processing rendering function. Defined
	// inside the function scope so as to not have to
	// pass the parameters around between functions.
	function renderProcessing(processing) {  
		processing.size(svWidth + hueWidth, bodyHeight + swatchHeight);
		processing.smooth();
		processing.noFill();
		processing.strokeWeight(1);
		processing.stroke(45, 45, 45);
			
		// Draw function.	
	 	processing.draw = function() {  	
			if (pixelsStale == true) {
				processing.loadPixels();			
				for (var x = 0; x < svWidth; x++) {
				  for (var y = 0; y < bodyHeight; y++) {
				    var color = computeValueColor(x, y);
					var loc = x + (y * (svWidth + hueWidth));
					processing.pixels.setPixel(loc, processing.color(color.r, color.g, color.b));
				  }
				}
				for (var x = 0; x < hueWidth; x++) {
				  for (var y = 0; y < bodyHeight; y++) {
				    var color = computeHueColor(x, y);	
					var loc = (x + svWidth) + (y * (svWidth + hueWidth));
					processing.pixels.setPixel(loc, processing.color(color.r, color.g, color.b));
				  }
				}		
				for (var x = 0; x < (hueWidth + svWidth); x++) {
				  for (var y = 0; y < swatchHeight; y++) {
				    var loc = x + ((y + bodyHeight) * (svWidth + hueWidth));
					processing.pixels.setPixel(
						loc, processing.color(params.valueR, params.valueG, params.valueB));	
				  }
				}		
				processing.updatePixels(); 	
				pixelsStale = false;
			}
			processing.ellipse(lastClickedX, lastClickedY, 8, 8);
	 	};
	 	
	 	// Mouse pressed function.
	 	processing.mousePressed = function() {
	 		if (processing.mouseY > bodyHeight) {
	 			return;
	 		}
			if (processing.mouseX <= svWidth) {
				setValueColor(processing.mouseX, processing.mouseY);
				lastClickedX = processing.mouseX;
				lastClickedY = processing.mouseY;
			} else {
				setHueColor(processing.mouseX, processing.mouseY);
				lastClickedX = svWidth - 5;
				lastClickedY = 5;
			}	
			pixelsStale = true;
		};
	}
}

// Returns the current value of the red channel
// for the specified canvasId.
function getRedValue(canvasId) {
	for (var i=0; i<colorPickers.length; i++) {
		var picker = colorPickers[i];
		if(picker.canvasId == canvasId) {
			return picker.params.valueR;
		}
	}
	return 0;
}

// Returns the current value of the green channel
// for the specified canvasId.
function getGreenValue(canvasId) {
	for (var i=0; i<colorPickers.length; i++) {
		var picker = colorPickers[i];
		if(picker.canvasId == canvasId) {
			return picker.params.valueG;
		}
	}
	return 0;
}

// Returns the current value of the blue channel
// for the specified canvasId.
function getBlueValue(canvasId) {
	for (var i=0; i<colorPickers.length; i++) {
		var picker = colorPickers[i];
		if(picker.canvasId == canvasId) {
			return picker.params.valueB;
		}
	}
	return 0;
}